package com.yy.permissions_general.security.config;


import com.yy.permissions_general.security.UserDetailsServiceImpl;
import com.yy.permissions_general.security.filter.VerifyCodeFilter;
import com.yy.permissions_general.security.handler.*;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;

import javax.annotation.Resource;

/**
 * @Description: SpringSecurity 配置
 * @Author: YY
 * @Date: 2021/11/3
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter  {

    @Resource
    private UserDetailsServiceImpl userDetailsService;
    /**
     * 验证码拦截器
     */
    @Resource
    private VerifyCodeFilter verifyCodeFilter;

    /**
     * 登录成功逻辑
     */
    @Resource
    private MyAuthenticationSuccessHandler myAuthenticationSuccessHandler;

    /**
     * 登录失败逻辑
     */
    @Resource
    private MyAuthenticationFailureHandler authenticationFailureHandler;
    /**
     * 退出登录逻辑
     */
    @Resource
    private MyLogoutSuccessHandler logoutSuccessHandler;

    @Resource
    private  MyLogoutHandler securityLogoutHandler;

    @Resource
    private SecureSessionExpiredHandler securityExpiredSessionHandler;

    @Resource
    private MyRememberMeHandler rememberMeHandler;

    /**
     * remember me redis持久化
     */
    @Resource
    private PersistentTokenRepository securityUserTokenService;

//    @Resource
//    private SecureUserTokenService secureUserTokenService;

    /**
     * 无权限拦截器
     */
    @Resource
    private RestAuthenticationEntryPoint restAuthenticationEntryPoint;

    /**
     * 无权访问 JSON 格式的数据
     */
    @Resource
    private RestfulAccessDeniedHandler accessDeniedHandler;

    /**
     * 用于统计用户在线
     */
    @Resource
    private SessionRegistry sessionRegistry;

    /**
     * anyRequest          |   匹配所有请求路径
     * access              |   SpringEl表达式结果为true时可以访问
     * anonymous           |   匿名可以访问
     * denyAll             |   用户不能访问
     * fullyAuthenticated  |   用户完全认证可以访问（非remember-me下自动登录）
     * hasPermission     |   如果有参数，参数表示权限，则其中任何一个权限可以访问
     * hasAnyRole          |   如果有参数，参数表示角色，则其中任何一个角色可以访问
     * hasAuthority        |   如果有参数，参数表示权限，则其权限可以访问
     * hasIpAddress        |   如果有参数，参数表示IP地址，如果用户IP和参数匹配，则可以访问
     * hasRole             |   如果有参数，参数表示角色，则其角色可以访问
     * permitAll           |   用户可以任意访问
     * rememberMe          |   允许通过remember-me登录的用户访问
     * authenticated       |   用户登录后可访问
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.addFilterBefore(verifyCodeFilter, UsernamePasswordAuthenticationFilter.class);
        //配置url的访问权限
        http.authorizeRequests()
                .antMatchers("/captcha","/static/**","/view/login","/login","/","/view/error/**").permitAll()
                .anyRequest().authenticated();
        //未登陆时返回 JSON 格式的数据给前端
        http.httpBasic().authenticationEntryPoint(restAuthenticationEntryPoint);
        // 无权访问 JSON 格式的数据
        http.exceptionHandling().accessDeniedHandler(accessDeniedHandler);
        //配置没有权限访问跳转自定义页面
        http.exceptionHandling().accessDeniedPage("/view/error/403");
        //使用自定义的登录窗口
        http.formLogin()
                .loginPage("/view/login")//登录页面
                .loginProcessingUrl("/login")//登录访问路径
                // 登录成功
                .successHandler(myAuthenticationSuccessHandler)
                // 登录失败
                .failureHandler(authenticationFailureHandler);
        http.sessionManagement().invalidSessionUrl("/view/login");
        //实现注销 （会清空session）
        http.logout()
                .logoutUrl("/logout") //只能是post方法，/mylogout是前端的请求
                .logoutSuccessUrl("/view/login")
//                .deleteCookies("XIAO-YI")// 删除名为'XIAOYI'的cookie
                .deleteCookies("JSESSIONID")
                .addLogoutHandler(securityLogoutHandler)
                .logoutSuccessHandler(logoutSuccessHandler);
        //记住我
        http.rememberMe()
                .rememberMeParameter("rememberMe")
                .rememberMeCookieName("rememberMeToken")
                .authenticationSuccessHandler(rememberMeHandler)
                .tokenRepository(securityUserTokenService)
                .key("XIAO-YI")
                .and()
                .sessionManagement()
                .sessionFixation()
                .migrateSession()
                // 在需要使用到session时才创建session
                .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
                // 同时登陆多个只保留一个
                .maximumSessions(1)
                .maxSessionsPreventsLogin(false)
                // 踢出用户操作
                .expiredSessionStrategy(securityExpiredSessionHandler)
                // 用于统计在线
                .sessionRegistry(sessionRegistry);

        //关闭csrf保护功能  取消跨站请求伪造防护
        http.csrf().disable();
        // 禁用缓存
        http.headers().cacheControl();
        http.headers().frameOptions().sameOrigin();
    }

    /**
     * 身份认证接口
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(new MyPasswordEncoderHandler());
    }


}
